package fr.inserm.umr915.vcfannotator.bio;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.URL;
import java.net.URLEncoder;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class BiodasSequenceProvider
	extends DefaultHandler
	implements SequenceProvider 
	{
	static Log LOG=LogFactory.getLog("vcf.annotator");
	private String dasBaseUrl=null;
	private String assembly=null;
	private ByteArrayOutputStream baos=null;
	int reserve=100000;
	private SAXParser parser;
	private int nRetry=10;
	private long sleep=10*1000;
	
	public BiodasSequenceProvider(String dasBaseUrl,String assembly)
		{
		this.dasBaseUrl=dasBaseUrl;
		this.assembly=assembly;
		SAXParserFactory f=SAXParserFactory.newInstance();
		f.setSchema(null);
		f.setNamespaceAware(false);
		f.setValidating(false);
		try
			{
			this.parser=f.newSAXParser();
			}
		catch(Exception err)
			{
			throw new RuntimeException(err);
			}
		}

	 @Override
	public void startDocument() throws SAXException
		{
		baos=null;
		}
	
	 public InputSource resolveEntity (String publicId, String systemId)
         {
         return new InputSource(new StringReader(""));
         }
	
	@Override
    public void startElement(String uri, String localName, String name,
            Attributes attributes) throws SAXException
        {
        if(name.equals("DNA"))
            {
            this.baos=new ByteArrayOutputStream(this.reserve);
            }
        }
	
    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException
        {
        if(this.baos==null) return;
        for(int i=0;i< length;++i)
            {
            char c= Character.toUpperCase(ch[start+i]);
            if(Character.isWhitespace(c)) continue;
            this.baos.write((byte)c);
            }
        }

    
   
    

	public GenomicSequence getGenomicSequence(String chrom, int chromStart0, int chromEnd0)
			throws IOException
		{
		if(chromStart0 <0 || chromStart0 >=chromEnd0)
			{
			throw new IllegalArgumentException("Error in start/end");
			}
		this.reserve=(1+(chromEnd0-chromStart0));
		this.baos=null;
		try
			{
			InputStream in=null;
			Exception error=null;
    		String uri=dasBaseUrl+"/"+ this.assembly+ "/dna?segment="+
    			URLEncoder.encode(chrom+":"+(chromStart0+1)+","+(chromEnd0+2), "UTF-8")
    			;
    		LOG.info(uri);
    		for(int i=0;i< this.nRetry;++i)
    			{
    			try {
    				URL url=new URL(uri);
    				in=url.openStream();
    				break;
    				}
    			catch (Exception e)
    				{
    				LOG.info(e.getMessage());
    				try {Thread.sleep(this.sleep);} catch (Exception e2) {}
    				error=e;
    				}
    			}
    		if(in==null) throw new IOException(error);
			
			
			this.parser.parse(in, this);
			in.close();
			GenomicSequence g= new GenomicSequence(
				chrom,
				chromStart0,
				this.baos.toByteArray()
				);
			this.baos=null;
			return g;
			}
		catch (SAXException err)
			{
			throw new IOException(err);
			}
		
		}
	
	@Override
	public GenomicSequence getGenomicSequence(String chrom)
	throws IOException
		{
		this.baos=null;
		this.reserve=200000000;
		try
			{
			InputStream in=null;
			Exception error=null;
    		String uri=dasBaseUrl+"/"+ this.assembly+ "/dna?segment="+
    			URLEncoder.encode(chrom, "UTF-8")
    			;
    		LOG.info(uri);
    		for(int i=0;i< this.nRetry;++i)
    			{
    			try {
    				URL url=new URL(uri);
    				in=url.openStream();
    				break;
    				}
    			catch (Exception e)
    				{
    				try {Thread.sleep(this.sleep);} catch (Exception e2) {}
    				error=e;
    				}
    			}
    		if(in==null) throw new IOException(error);
			
			this.parser.parse(in, this);
			in.close();
			GenomicSequence g= new GenomicSequence(chrom,0,this.baos.toByteArray());
			this.baos=null;
			return g;
			}
		catch (SAXException err)
			{
			throw new IOException(err);
			}
		
		}

	public void setRetryCount(int nRetry) {
		this.nRetry = nRetry;
		}
	
	public void setSleepMilliSeconds(long sleep)
		{
		this.sleep = sleep;
		}
	
	@Override
	public void close()
		{
		this.baos=null;
		}

	

}
